<?php

/**
 * Copyright (c) 2015-2022
 *
 * All rights reserved.
 *
 * @author Hon(陈烁临)
 * @email 2275604210@qq.com
 * @create date 2023-02-08 16:12:35
 * @modify date 2023-02-08 16:12:35
 * @desc [description]
 */

namespace app\command\dev;

use support\exception\BusinessException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;


class EnumsGenerate extends Command
{
  protected static $defaultName = 'dev:enumGenerate';
  protected static $defaultDescription = '更据枚举配置生成枚举';

  private $enums = [];
  private $list = false;

  protected function configure(): void
  {
    $this
      ->addOption(
        'list',
        'l',
        InputOption::VALUE_NONE,
        '列出所有枚举?'
      )
      ->addArgument('enums',InputArgument::IS_ARRAY)
  ;
  }


  private function generateEnumTs($enumData)
  {
    if (!in_array('withEnum', $enumData['ts'])) {
      return;
    }
    $module = $enumData['module'];
    $enumName = $enumData['enumName'];
    $comment = $enumData['comment'];
    $data = $enumData['data'];
    $file_path = base_path("../src/enumconfig/{$module}/{$enumName}.ts");

    if (!file_exists(dirname($file_path))) {
      mkdir(dirname($file_path), 0777, true);
    }
    $date = date('Y-m-d H:i:s');
    $code = "// 本文件代码由EnumsGenerateCommand自动生成，请勿手动修改

import { withEnum } from '/@/utils/enum';";
    $moreField  = [];
    $keys = [];
    // $edata = [];
    $enumData = [];
    foreach ($data as $item) {
      $key = ucfirst($item['key']);
      $keys[] = "'$key'";
      array_forget($item, 'key');
      // $edata[$key] = $item;
      $str = "
  $key: {
    text: '{$item['text']}',
    value: '{$item['value']}',";
      array_forget($item, ['value', 'text']);
      if (!empty(array_keys($item))) {

        // array_push($edata[$key], $item);
        foreach ($item as $k => $v) {
          if (!in_array($k, $moreField)) {
            $moreField[] = $k;
          }
          $str .= "
    $k: '$v',";
        }
      }
      $str .= "
  }";
      $enumData[] = $str;
    }
    $moreStr = "";
    if (!empty($moreField)) {
      $mors = array_map(function ($value) {
        return "$value?: string";
      }, $moreField);
      $moreStr = ", { " . implode(',', $mors) . " }";
    }
    $kstr = implode(' | ', $keys);
    // $js_data_str = json_encode($edata, JSON_UNESCAPED_UNICODE);
    $js_data_str = implode(',', $enumData) . ',';
    $code .= "
type K = {$kstr};
/**
 * {$comment}
 */
export const {$enumName} = withEnum<K, string{$moreStr}>({{$js_data_str}
});
";

    $file = fopen($file_path, 'w');
    fwrite($file, $code);
    fclose($file);
    dump("$file_path 创建成功");
  }

  private function generateEnumClass($enumData)
  {
    if (!in_array('EnumClass', $enumData['ts'])) {
      return;
    }
    $module = $enumData['module'];
    $enumName = $enumData['enumName'];
    $comment = $enumData['comment'];
    $data = $enumData['data'];
    $file_path = base_path("../src/enumconfig/{$module}/{$enumName}Enum.ts");

    if (!file_exists(dirname($file_path))) {
      mkdir(dirname($file_path), 0777, true);
    }
    $code = "/* eslint-disable prettier/prettier */
// 本文件代码由EnumsGenerateCommand自动生成，请勿手动修改

import EnumClass from '/@/utils/EnumClass';";
    $keys = [];
    $values = [];
    // $edata = [];
    $items = [];
    $extra = [];
    foreach ($data as $item) {
      $key = $item['key'];
      $value = $item['value'];
      $keys[] = "'$key'";
      $values[] = is_int($value)?$value:"'$value'";
      
      $extraKeys = array_diff(array_keys($item), ['key', 'value', 'text']);

      foreach ($extraKeys as $extraKey) {
        $extraValue = data_get($item, $extraKey);
        
        $extraType = is_string($extraValue) ? 'string' : (is_int($extraValue) ? 'number' : 'any');
        $extraType = data_get($extra, $extraKey, $extraType) == $extraType ? $extraType: 'any';
        $extra[$extraKey] = $extraType;
      }
      // $extra
    }
    $extrastr = '';
    foreach ($extra as $extraKey => $extraType) {
      $extrastr .= "\n$extraKey?: $extraType";
    }
    $kstr = implode(' | ', $keys);
    $vstr = implode(' | ', $values);
    $items = json_encode($data, JSON_UNESCAPED_UNICODE);
    $code .= "
type Key = $kstr;
type Value = $vstr;
type Extra = {
  $extrastr
};
/**
 * {$comment}
 */
const {$enumName}Enum = EnumClass.make<Key, Value, Extra>(
  $items
);
export default {$enumName}Enum;
export type {$enumName}Value = Value;
export type {$enumName}Key = Key;
";

    $file = fopen($file_path, 'w');
    fwrite($file, $code);
    fclose($file);
    dump("$file_path 创建成功");
  }

  private function generateEnumPhp($enumData)
  {
    $module = $enumData['module'];
    $enumName = ucfirst($enumData['enumName']);
    $enumName .= 'Enum';
    $comment = $enumData['comment'];
    $data = $enumData['data'];
    $file_path = app_path("enums/{$module}/{$enumName}.php");

    if (!file_exists(dirname($file_path))) {
      mkdir(dirname($file_path), 0777, true);
    }
    $date = date('Y-m-d H:i:s');
    $moduleSpace = str_replace('/', '\\', $module);
    $code = "<?php
// 本文件代码由EnumsGenerateCommand自动生成，请勿手动修改
namespace app\\enums\\$moduleSpace;

use app\\enums\AbstractEnum;

/**
* $comment
*/
class $enumName extends AbstractEnum
{";

    foreach ($data as $item) {
      $key = $item['key'];
      $value = $item['value'];
      $text = $item['text'];
      if (is_string($value)) {
        $value = "'{$value}'";
      }
      $key = strtoupper($key);
      $code .= "
  /**
   * $text - [$comment]
   */
  const $key = $value;
  /**
   * $text - [$comment]
   */
  const {$key}__TEXT = '$text';";
    }

    $encoded_data = json_encode($data, JSON_UNESCAPED_UNICODE);
    $code .= "

  public static function getData() { return json_decode('$encoded_data', true);}
}";


    $file = fopen($file_path, 'w');
    fwrite($file, $code);
    fclose($file);
    dump("$file_path 创建成功");
  }


  protected function getMessageForFinish()
  {
    return 'Successful!';
  }

  protected function generateEnum(string $module, array $enums, $module_pre = '')
  {
    if ($module == 'app') return false;
    foreach ($enums as $enumName => $enumConfig) {
      if ($enumName == 'app') {
        continue;
      }
      $newModule = $module_pre == '' ? $module : $module_pre . '/' . $module;
      if (isset($enumConfig['comment']) && isset($enumConfig['data']) && is_array($enumConfig['data'])) {
        $enumData = [
          'module' => $newModule,
          'enumName' => $enumName,
          'comment' => $enumConfig['comment'],
          'data' => $enumConfig['data'],
          'ts' => $enumConfig['ts']?? ['withEnum'],
        ];

        if ($this->list) {
          dump(">>>> {$newModule}/{$enumName} :{$enumData['comment']}");
        } elseif (empty($this->enums) || in_array("{$newModule}/{$enumName}", $this->enums)) {
          dump(">>>> 开始生成 {$newModule}/{$enumName} :{$enumData['comment']}");
          $this->generateEnumClass($enumData);
          $this->generateEnumTs($enumData);
          $this->generateEnumPhp($enumData);
          
          dump("<<<< 生成完成 {$newModule}/{$enumName} :{$enumData['comment']}");
        }
      } else {
        $this->generateEnum($enumName, $enumConfig, $newModule);
      }
    }
  }



  protected function execute(InputInterface $input, OutputInterface $output)
  {
    if (env('APP_DEBUG', false) != true) {
      throw new BusinessException('dev:enumGenerate 命令只能在开发环境执行！');
    }

    $this->enums = $input->getArgument('enums');
    $this->list = $input->getOption('list');
    $config = config('enums');

    foreach ($config as $module => $enums) {
      $this->generateEnum($module, $enums);
    }

    return self::SUCCESS;
  }
}
